#include "AtencionUsuario.h"
#include <sstream>

AtencionUsuario::AtencionUsuario(int fd, int fd_av, std::string nom_arch
		, std::map<unsigned int, Clase*> *clases, Mutex *mutex) {
	this->socket = new Socket(fd);
	this->socket_av = new Socket(fd_av);
	this->atendiendo = true;
	this->nom_arch = nom_arch;
	this->clases = clases;
	codigo_clase_a_iniciar = 0;
	this->mutex = mutex;
}

bool AtencionUsuario::getAtendiendo() {
	return this->atendiendo;
}

void AtencionUsuario::run() {
	int tam_bloque_ini = sizeof(int) + sizeof(char);
	Clase *clase = NULL;
	unsigned int tam_arg;
	char buffer[TAM_BLOQUE_INI];
	std::string clave;
	std::string nombre_usuario;
	std::string codClase;
	char cod_op;
	bool profesor = false;
	bool alumno = false;
	bool clase_iniciada = false;
	bool clase_terminada = true;
	char *argumentos = NULL;
	while(1) {
		argumentos = NULL;
		if(!this->socket->recibir(buffer, tam_bloque_ini)) {
			std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
			break;
		}
		cod_op = buffer[0];
		if((cod_op != '0') && (cod_op != '1') && (!profesor) && (!alumno)) {
			std::cerr << "Codigo de Operacion Invalido" << std::endl;
			break;
		}
		tam_arg = *(unsigned int*)&buffer[1];
		memmove(buffer, &buffer[4], TAM_BLOQUE_INI - 4);
		argumentos = new char[tam_arg];
		if(tam_arg > 0) {
			if(!this->socket->recibir(argumentos, tam_arg)) {
				std::cout << "No se pudo recibir " << std::endl;
				delete[] argumentos;
				break;
			}
		}
		// Login, que devuelva un boolean, que represente si esta logeado
		// Si el cod_op es 0 entonces es un instructor, crea una clase
		if(cod_op == '0') {
			std::cout << "Soy un instructor, yo puedo crear e iniciar clases" << std::endl;
			profesor = true;
			alumno = false;
			nombre_usuario = argumentos;
			clave = &argumentos[nombre_usuario.size() + 1];
			std::cout << "Nombre de usuario: " << nombre_usuario << std::endl;
			std::cout << "Clave: " << clave << std::endl;
		}
		// Si el cod_op es 1 entonces es un alumno
		if(cod_op == '1') {
			std::cout << "Soy un alumno, yo puedo inscribirme a clases" << std::endl;
			alumno = true;
			profesor = false;
			nombre_usuario = argumentos;
			clave = &argumentos[nombre_usuario.size() + 1];
			std::cout << "Nombre de usuario: " << nombre_usuario << std::endl;
			std::cout << "Clave: " << clave << std::endl;
		}
		if(profesor) {
			switch(cod_op) {
			// Inicia una clase
			case('A'): {
				std::cout << "Recibi para iniciar la clase" << std::endl;
				codClase = argumentos;
				std::stringstream ss(codClase);
				ss >> codigo_clase_a_iniciar;
				std::cout << "Clasea iniciar: " << codClase << " Integer " << codigo_clase_a_iniciar << std::endl;
				if(this->buscar(codigo_clase_a_iniciar)) {
					(*(clases))[codigo_clase_a_iniciar]->setSocket_Instr(socket_av);
					this->avisar_aAlumnos(codigo_clase_a_iniciar);
					clase_iniciada = true;
					clase_terminada = false;
					((*(clases))[codigo_clase_a_iniciar])->iniciarClase();
					((*(clases))[codigo_clase_a_iniciar])->start();
				}
				break;
			}
			// Termina una clase, la unica q tiene q estar dandose
			case('B'): {
				codClase = argumentos;
				std::stringstream ss(codClase);
				int cod_clase_a_terminar;
				ss >> cod_clase_a_terminar;
				if(cod_clase_a_terminar == codigo_clase_a_iniciar) {
					std::cout << "Clase terminada" << std::endl;
					clase_iniciada = false;
					clase_terminada = true;
					((*(clases))[codigo_clase_a_iniciar])->terminarClase();
					((*(clases))[codigo_clase_a_iniciar])->join();
					// Si termina una clase el codigo_clase_a_iniciar sera 0
					codigo_clase_a_iniciar = 0;
				}
				break;
			}
			// Crea una clase
			case('C'): {
				std::string nombreClase;
				codClase = argumentos;
				int cod_Clase = 0;
				std::stringstream ss(codClase);
				ss >> cod_Clase;
				nombreClase = &argumentos[codClase.size() + 1];
				if(this->buscar(cod_Clase)) {
					std::cerr << "Clase existente" << std::endl;
					// Me fijo quienes estan inscriptos en la clase ya creado
					(*(this->clases))[cod_Clase]->verInscriptos();
				}
				else {
					clase = new Clase(nombreClase, cod_Clase, &clase_terminada);
					mutex->lock();
					(*(this->clases))[cod_Clase] = clase;
					mutex->unlock();
					std::cout << "clase creada: " << nombreClase << " " << cod_Clase << std::endl;
				}
				break;
			}
			}
		}
		if(alumno) {
			switch(cod_op) {
			// Inscribirse a una clase
			case('A'): {
				unsigned int codClase = 0;
				std::stringstream ss(argumentos);
				ss >> codClase;
				std::cout << nombre_usuario << " quiere inscribirse a la clase: " << codClase << std::endl;
				if(this->buscar(codClase)) {
					if(!this->buscarInscripcion(codClase)) {
						this->inscribirAlumno(codClase, nombre_usuario);
						//(*(clases))[codClase]->agregarSocketAlumno(this->socket);
						(*(clases))[codClase]->agregarSocketAlumno_AV(this->socket_av);
						// Aca tengo q instanciar un objeto de una clase preguntadorade Aula
					}
					else {
						std::cerr << "Ya estas incripto a esta clase" << std::endl;
					}
				}
				else
					std::cerr << "No existe la clase" << std::endl;
				break;
			}
			case('B'): {

			}
			}
		}
		delete[] argumentos;
	}
	atendiendo = false;
}

 // Avisa a los alumnos que la clase va a empezar
int AtencionUsuario::avisar_aAlumnos(const unsigned int codClase) {
	char cod_op = '0';
	std::string codClase_str;
	std::stringstream ss;
	ss << codClase;
	ss >> codClase_str;
	int tam_arg = codClase_str.size() + 1;
	std::list<Socket*>::iterator it;
	mutex->lock();
	it = (((*(clases))[codClase])->getSocket_alumnos_av()).begin();
	for(unsigned int i = 0; i < (*(clases))[codClase]->getSocket_alumnos_av().size(); i++) {
		if(!(*it)->enviar(&cod_op, sizeof(cod_op))) {
			std::cerr << "No se pudo enviar 1" << std::endl;
			return -1;
		}
		if(!(*it)->enviar(&tam_arg, sizeof(tam_arg))) {
			std::cerr << "No se pudo enviar 2" << std::endl;
			return -1;
		}
		if(!(*it)->enviar(codClase_str.c_str(), codClase_str.size() + 1)) {
			std::cerr << "No se pudo enviar 3" << std::endl;
			return -1;
		}
		it++;
	}
	/*for(it = (((*(clases))[codClase])->getSocket_alumnos()).begin(); it != (((*(clases))[codClase])->getSocket_alumnos()).end(); ++it) {
		std::cout << (*(clases))[codClase]->getSocket_alumnos().size() << std::endl;
		//std::cout << "Mensaje" << std::endl;
		if(!(*it)->enviar(&cod_op, sizeof(cod_op))) {
			std::cerr << "No se pudo enviar 1" << std::endl;
			return -1;
		}
		if(!(*it)->enviar(&tam_arg, sizeof(tam_arg))) {
			std::cerr << "No se pudo enviar 2" << std::endl;
			return -1;
		}
		if(!(*it)->enviar(codClase_str.c_str(), codClase_str.size() + 1)) {
			std::cerr << "No se pudo enviar 3" << std::endl;
			return -1;
		}
	}*/
	mutex->unlock();
	return 0;
}

bool AtencionUsuario::buscarInscripcion(unsigned int codClase) {
	std::list<unsigned int>::iterator it;
	mutex->lock();
	for(it = inscripciones.begin(); it != inscripciones.end(); ++it) {
		if((*it) == codClase) {
			mutex->unlock();
			return true;
		}
	}
	mutex->unlock();
	return false;
}

// Busca si existe la clave, si existe retorna true
bool AtencionUsuario::buscar(const unsigned int codClase) {
	mutex->lock();
	if(this->clases->find(codClase) != this->clases->end()) {
		mutex->unlock();
		return true;
	}
	mutex->unlock();
	return false;
}

void AtencionUsuario::inscribirAlumno(const unsigned int codClase, std::string nombre_alumno) {
	std::cout << "Inscripto con exito" << std::endl;
	// Inscribo un alumno en la clase con codigo "codClase"
	mutex->lock();
	this->clases->find(codClase)->second->inscribirAlumno(nombre_alumno);
	inscripciones.push_back(codClase);
	mutex->unlock();
}

AtencionUsuario::~AtencionUsuario() {
	this->socket->shutDown();
	delete this->socket;
	this->socket_av->shutDown();
	delete this->socket_av;
	this->join();
	if(this->codigo_clase_a_iniciar > 0)
		((*clases))[codigo_clase_a_iniciar]->terminarClase();
}
